package faiaMod;

import java.util.Vector;

import frsf.cidisi.exercise.modelo.search.Agente;
import frsf.cidisi.exercise.modelo.search.AgenteEstado;
import frsf.cidisi.exercise.modelo.search.Ambiente;
import frsf.cidisi.exercise.modelo.search.AmbienteEstado;
import frsf.cidisi.exercise.modelo.util.Coordenada;
import frsf.cidisi.faia.agent.Action;
import frsf.cidisi.faia.agent.Agent;
import frsf.cidisi.faia.agent.GoalBasedAgent;
import frsf.cidisi.faia.agent.Perception;
import frsf.cidisi.faia.environment.Environment;
import frsf.cidisi.faia.simulator.Simulator;
import frsf.cidisi.faia.simulator.events.EventType;
import frsf.cidisi.faia.simulator.events.SimulatorEventNotifier;
import frsf.cidisi.faia.solver.search.NTree;

public abstract class GoalBasedAgentSimulatorMod extends Simulator {

    /**
     * 
     * @param environment
     */
    public GoalBasedAgentSimulatorMod(Environment environment, Vector<Agent> agents) {
    	super(environment, agents);
    }

    public GoalBasedAgentSimulatorMod(Environment environment, Agent agent) {
        Vector<Agent> ags = new Vector<Agent>();
        ags.add(agent);

        this.environment = environment;
        this.agents = ags;
    }

    @Override
    public void start() {

        System.out.println("----------------------------------------------------");
        System.out.println("--- " + this.getSimulatorName() + " ---");
        System.out.println("----------------------------------------------------");
        System.out.println();

        Perception perception;
        GoalBasedAgent agent;

        agent = (GoalBasedAgent) this.getAgents().firstElement();

        /*
         * Simulation starts. The environment sends perceptions to the agent, and
         * it returns actions. The loop condition evaluation is placed at the end.
         * This works even when the agent starts with a goal state (see agentSucceeded
         * method in the SearchBasedAgentSimulator).
         */
        AgenteEstado estadoAgente;
        AmbienteEstado estadoAmbiente = ((Ambiente)this.environment).getEnvironmentState();
        int j = estadoAmbiente.getCantNiveles();
        System.out.println("Cantidad de niveles del ambiente: " + j);
        
        for(int k = 0; k < estadoAmbiente.getCantNiveles(); k++ ){
        	
        	((AmbienteEstado)this.environment.getEnvironmentState()).cambiarNivel(k);
        	if(k > 0){
        		estadoAgente = ((AgenteEstado)agent.getAgentState());
        		Integer Y = estadoAgente.getPosAgente().getY();
        		estadoAgente.setposAgente(new Coordenada(0, Y));
        		estadoAgente.setEntradaAgente(new Coordenada(0, Y));
        		estadoAgente.setTieneLlave(false);
        	}
	            System.out.println("------------------------------------");
	            System.out.println("Iniciando NIVEL: " + (k+1));
	
	            System.out.println("Sending perception to agent...");
	            perception = this.getPercept();
	            agent.see(perception);
	            System.out.println("Perception: " + perception);
	
	            System.out.println("Agent State: \n" + agent.getAgentState());
	            System.out.println("Environment: \n" + environment);
	
	            System.out.println("Asking the agent for a list of actions...");
	            Vector<NTree> acciones = ((Agente)agent).selectActionMod();
	
	        // Check what happened, if agent has reached the goal or not.
	        int i = 0;
	        
	        System.out.println("Actions returned:");
	            
	        do {
	        	
	        	if(acciones == null){
	        		break;
	        	}
	        	
	        	this.actionReturned(agent, acciones.elementAt(i).getAction());
	        	
	        	System.out.println(" " + acciones.elementAt(i).getAction());
	        	
	        	i++;
	        } while (i < acciones.size());
        
	        if (this.agentSucceeded(acciones.lastElement().getAction())){
	        	System.out.println("Agent has reached the goal!");
	        }
	        
	        else {
	        	System.out.println("ERROR: The simulation has finished, but the agent has not reached his goal.");
	        }
        }
        	
        // Leave a blank line
        System.out.println();
        
        // FIXME: This call can be moved to the Simulator class
        this.environment.close();

        // Launch simulationFinished event
        SimulatorEventNotifier.runEventHandlers(EventType.SimulationFinished, null);
    }
    
    
    /* Start para el problema 2 */
    public void start1(){
    	/*Ejercicio 2: Costo Uniforme.*/

        System.out.println("----------------------------------------------------");
        System.out.println("--- " + this.getSimulatorName() + " ---");
        System.out.println("----------------------------------------------------");
        System.out.println("EJERCICIO 2 - Costo uniforme.");
        System.out.println();

        Perception perception;
        GoalBasedAgent agent;

        agent = (GoalBasedAgent) this.getAgents().firstElement();

        /*
         * Simulation starts. The environment sends perceptions to the agent, and
         * it returns actions. The loop condition evaluation is placed at the end.
         * This works even when the agent starts with a goal state (see agentSucceeded
         * method in the SearchBasedAgentSimulator).
         */
        ((AmbienteEstado)this.environment.getEnvironmentState()).cambiarNivel(0);
	            System.out.println("------------------------------------");
	            System.out.println("Iniciando NIVEL: " + (1));
	
	            System.out.println("Sending perception to agent...");
	            perception = this.getPercept();
	            agent.see(perception);
	            System.out.println("Perception: " + perception);
	
	            System.out.println("Agent State: \n" + agent.getAgentState());
	            System.out.println("Environment: \n" + environment);
	
	            System.out.println("Asking the agent for a list of actions...");
	            
	            Vector<NTree> acciones = ((Agente)agent).selectActionMod2();
	
	        // Check what happened, if agent has reached the goal or not.
	        int i = 0;
	        
	        System.out.println("Actions returned:");
	            
	        do {
	        	
	        	if(acciones == null){
	        		break;
	        	}
	        	
	        	this.actionReturned(agent, acciones.elementAt(i).getAction());
	        	
	        	System.out.println(" " + acciones.elementAt(i).getAction());
	        	
	        	i++;
	        } while (i < acciones.size());
        
	        if (this.agentSucceeded(acciones.lastElement().getAction()))
	        	System.out.println("Agent has reached the goal!");
	        	        
	        else {
	        	System.out.println("ERROR: The simulation has finished, but the agent has not reached his goal.");
	        }
        
        	
        // Leave a blank line
        System.out.println();
        
        // FIXME: This call can be moved to the Simulator class
        this.environment.close();

        // Launch simulationFinished event
        SimulatorEventNotifier.runEventHandlers(EventType.SimulationFinished, null);
    }
    
    /* Start para el Problema 3 */
    public void start2(){
    	/*Ejercicio 3: Primero en profundidad.*/

        System.out.println("----------------------------------------------------");
        System.out.println("--- " + this.getSimulatorName() + " ---");
        System.out.println("----------------------------------------------------");
        System.out.println("EJERCICIO 3 - Primero en profundidad.");
        System.out.println();

        Perception perception;
        GoalBasedAgent agent;

        agent = (GoalBasedAgent) this.getAgents().firstElement();

        /*
         * Simulation starts. The environment sends perceptions to the agent, and
         * it returns actions. The loop condition evaluation is placed at the end.
         * This works even when the agent starts with a goal state (see agentSucceeded
         * method in the SearchBasedAgentSimulator).
         */
        ((AmbienteEstado)this.environment.getEnvironmentState()).cambiarNivel(1);
        
        //pongo al agente en la posicion para el nivel.
        
        ((AgenteEstado)agent.getAgentState()).setposAgente(new Coordenada(0, 3));
        
	            System.out.println("------------------------------------");
	            System.out.println("Iniciando NIVEL: " + 2);
	
	            System.out.println("Sending perception to agent...");
	            perception = this.getPercept();
	            agent.see(perception);
	            System.out.println("Perception: " + perception);
	
	            System.out.println("Agent State: \n" + agent.getAgentState());
	            System.out.println("Environment: \n" + environment);
	
	            System.out.println("Asking the agent for a list of actions...");
	            
	            Vector<NTree> acciones = ((Agente)agent).selectActionMod3();
	
	        // Check what happened, if agent has reached the goal or not.
	        int i = 0;
	        
	        System.out.println("Actions returned:");
	            
	        do {
	        	
	        	if(acciones == null){
	        		break;
	        	}
	        	
	        	this.actionReturned(agent, acciones.elementAt(i).getAction());
	        	
	        	System.out.println(" " + acciones.elementAt(i).getAction());
	        	
	        	i++;
	        } while (i < acciones.size());
        
	        if (this.agentSucceeded(acciones.lastElement().getAction()))
	        	System.out.println("Agent has reached the goal!");
	        	        
	        else {
	        	System.out.println("ERROR: The simulation has finished, but the agent has not reached his goal.");
	        }
        
        	
        // Leave a blank line
        System.out.println();
        
        // FIXME: This call can be moved to the Simulator class
        this.environment.close();

        // Launch simulationFinished event
        SimulatorEventNotifier.runEventHandlers(EventType.SimulationFinished, null);
    }

    /**
     * Here we update the state of the agent and the real state of the
     * simulator.
     * @param action
     */
    protected void updateState(Action action) {
        this.getEnvironment().updateState(((GoalBasedAgent) agents.elementAt(0)).getAgentState(), action);
    }

    public abstract boolean agentSucceeded(Action action);

    public abstract boolean agentFailed(Action action);

    /**
     * This method is executed in the mail loop of the simulation when the
     * agent returns an action.
     * @param agent
     * @param action
     */
    public abstract void actionReturned(Agent agent, Action action);

    /**
     * @return The name of the simulator, e.g. 'SearchBasedAgentSimulator'
     */
    public abstract String getSimulatorName();
}
